﻿#include "标头.h"
#define WINDOW_NAME1 "反向投影图" 

void Quick_demo::demo(Mat& image) {
	{
		Mat hsv, gray;
		cvtColor(image, hsv, COLOR_BGR2HSV);//转hsv
		cvtColor(image, gray, COLOR_BGR2GRAY);//转灰度
		imshow("HSV", hsv);
		imshow("GRAY", gray);
		imwrite("E:/opencv4.8.0/study/hsv.png", hsv);
		imwrite("E:/opencv4.8.0/study/gray.png", gray);
	}
}

void Quick_demo::mat_creation_demo(Mat& image) {
	Mat m1;
	Mat m2;
	Mat m3;
	//克隆一份image对象给m1
	m1 = image.clone();
	//拷贝构造
	image.copyTo(m2);
	//创建一张空白图像
	m3 = Mat::zeros(Size(400, 400), CV_8UC3);//通常用来处理BGR图像
	//cout << m3 << endl;
	m3 = Scalar(0, 255, 0);//改变每个通道的数值
	Mat m4;
	m4 = m3;
	m4 = Scalar(211, 111, 189);
	imshow("BGR", m3);
}
//遍历每个像素点
void Quick_demo::pixel_visit_demo(Mat& image) {
	//数组方式
	/*图像的列数就是宽度 图像的行数就是高度 */ 
	int width = image.rows;//rows和cols都是公有的成员变量
	int high = image.cols;
	int dims = image.channels();//获取通道数
	//for (int i = 0; i < width; i++) {
	//	for (int j = 0; j < high; j++) {
	//		if (dims == 1) {//灰度图像
	//			//获取像素值(image.at<class>(i,j))并将进行赋值
	//			int pv = image.at<uchar>(i,j);//访问第几行第几列的像素
	//			image.at<uchar>(i, j) = 255 - pv;//并进行赋值
	//		}

	//		else if (dims == 3) {//彩色图像
	//			Vec3b bgr = image.at<Vec3b>(i, j);//返回值是一个Vec3b类型;
	//			//修改每个像素的数值时先得到矩阵中拿到对应的数值 然后 在用储存在bgr数值里的每个值进行修改赋值
	//			image.at<Vec3b>(i, j)[0] = 255 - bgr[0];
	//			image.at<Vec3b>(i, j)[1] = 255 - bgr[1];
	//			image.at<Vec3b>(i, j)[2] = 255 - bgr[2];
	//		}
	//	}
	//}
	//saturate_cast<>()改变数据类型
	
	//利用智能指针
	for (int i = 0; i < width; i++) {
		uchar* ptr = image.ptr<uchar>(i);
		for (int j = 0; j < high; j++) {
			if (dims == 1) {//灰度图像
				*ptr++ = 255 - *ptr;
			}

			else if (dims == 3) {//彩色图像
				*ptr++ = 255 - *ptr;
				*ptr++ = 255 - *ptr;
				*ptr++ = 255 - *ptr;
			}
		}
	}
	imshow("s", image);
}


//static void on_lightness(int light,void* userdata) {
//	Mat image = *((Mat*)userdata);
//	Mat tmp= Mat::zeros(image.size(), image.type());
//	//设置初始值
//	tmp = Scalar(light, light, light);
//	Mat dst = Mat::zeros(tmp.size(), tmp.type());
//	add(image, tmp, dst);
//	//subtract(src, m, dst);
//	//multiply(src, m, dst);
//	//divide(src, m, dst);
//	imshow("亮度调整后",dst);
//}

static void on_lightness(int light, void* userdata) {
	Mat image = *((Mat*)userdata);
	Mat tmp = Mat::zeros(image.size(), image.type());
	Mat dst = Mat::zeros(tmp.size(), tmp.type());
	//设置初始值
	tmp = Scalar(light, light, light);
	addWeighted(image, 1.0, tmp, 1.0, 0, dst);//addWeighted 函数的作用是对两个数组进行加权求和，公式为 \(dst = src1\times\alpha + src2\times\beta+\gamma\)
	imshow("亮度和对比度调整", dst);
}

static void on_contrast(int contrast, void* userdata) {//对比度调整本质上是对图像的像素值进行缩放操作
	                                                   //要实现对比度调整，需要根据对比度值对原图像的像素值进行缩放
	Mat image = *((Mat*)userdata);
	Mat tmp = Mat::zeros(image.size(), image.type());
	Mat dst = Mat::zeros(tmp.size(), tmp.type());
	double alpha = contrast / 50;
	addWeighted(image, alpha, tmp, 0, 0, dst);
	imshow("亮度和对比度调整", dst);
}

void Quick_demo::tracking_bar_demo(Mat& image) {
	int light = 50;
	int max_value = 100;
	int contrast = 100;
	namedWindow("亮度和对比度调整",WINDOW_AUTOSIZE);//需要创建一个滑块窗口
	createTrackbar("亮度", "亮度和对比度调整", &light, max_value, on_lightness,(void*)(&image));
	createTrackbar("对比度", "亮度和对比度调整", &contrast, 200, on_contrast, (void*)(&image));
	//1.createTrackbar()的第二个参数需要一个已经创建好的窗口来为亮度条提供窗口
	//2.每次用户滑动亮度条就会更新value的值使得亮度变化
	//3.void on_track（int,void*)
	//4.createTrackbar第五个参数是个函数指针
}

void Quick_demo::key_demo(Mat& image) {
	while (true) {
		Mat dst = Mat::zeros(image.size(),image.type());//先创建一个画布
		int c = waitKey();//返回值包含了与用户按键操作相关的重要信息(其返回值为int)
		if (c == 27) {
			break;
		}

		else if (c == 49) {//变灰度
			cout << c << endl;
			cvtColor(image, dst, COLOR_BGR2GRAY);
			
		}

		else if (c == 50) {//加亮度
			cout << c << endl;
			dst =  (50, 50, 50);
			add(image, dst, dst);			
		}

		imshow("key", dst);
	}

}

void Quick_demo::bitwise_demo(Mat& image) {
	Mat m1 = Mat::zeros(Size(256,256),CV_8UC3 );
	Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
	rectangle(m1, Rect(100, 100, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);//-1 填充
	rectangle(m2, Rect(100, 100, 80, 80), Scalar(255, 0, 255), -1, LINE_8, 0);
	Mat dst = Mat::zeros(image.size(), image.type());
	//bitwise_and(m1, m2, dst);
	bitwise_or(m1, m2, dst);
	imshow("bitwise", dst);
}

void Quick_demo::channels_demo(Mat& image) {
	Mat dst;
	vector<Mat> channel;
	split(image, channel);
	 channel[0] = Mat::zeros(image.size(), CV_8UC1);
	auto c2= channel[1];
	auto c3= channel[2];
	merge(channel, dst);
	imshow("merge", dst);
}

void Quick_demo::mixChannel(Mat& image) {
	Mat dst;
	dst.create(image.size(), image.type());//在传dst的时候先需对dst进行内存分配
	vector<int> from_to = { 0,2,1,1,2,0 };//BGR - RGB b 0 g 1 r 2(转换要符合映射通道规则）
	                                                    //在传dst的时候先需对dst进行内存分配
	mixChannels(&image, 1, &dst, 1, from_to.data(), 3);//第五个参数需要的式int*类型指针 from_to.data() 返回的是 int* 类型指针
	imshow("BGR - RGB", dst);
}

void  Quick_demo::inrange_demo(Mat& image) {
	//将绿幕扣下(用hsv图片扣)
	Mat hsv;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	namedWindow("抠图后", WINDOW_FREERATIO);
	Mat mask;
	inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255),mask);
	//创建一个红色背景并将人物扣上去
	Mat redback;
	redback = Mat::zeros(image.size(), image.type());
	redback = Scalar(0, 0, 255);
	imshow("huabu", redback);
	bitwise_not(mask, mask);//将mask中黑色的人物变白以用copy_to传递
	image.copyTo(redback,mask);
	//无掩码情况：当不提供 mask 参数时，copyTo 会将 src 图像的所有像素复制到 dst 中。若 dst 的大小和类型与 src 不同，dst 会自动调整。
	//有掩码情况：当提供 mask 参数时，copyTo 会遍历 mask 矩阵。对于 mask 中值为非零的像素位置，将 image 对应位置的像素复制到 redback 中；对于 mask 中值为零的像素位置，redback 对应位置的像素保持不变。
	imshow("抠图后", redback);
}

void Quick_demo::pixel_static_demo(Mat& image) {
	//minMaxLoc（）第一个参数必须为单通道
	double minval, maxval;
	Point minloc, maxloc;//Point 类型是一个非常基础且常用的类，用于表示二维平面上的一个

	//Vec3b是像素值（BGR三元组），不是单通道矩阵 Vec3b bgr = image.at<Vec3b>(i)访问的是像素

	//int dims = image.channels();//获取通道数
	//for (int i = 0; i < dims; i++) {
	//		Vec3b bgr = image.at<Vec3b>(i);
	//		minMaxLoc(bgr[i], &minval, &maxval, &minloc, &maxloc, Mat());
	//		cout << "minval: " <<  minval << "maxval: " <<  maxval << endl;
	//}
	vector<Mat> channel;
	split(image, channel);
	for (int i = 0; i < channel.size(); i++) {
		minMaxLoc(channel[i], &minval, &maxval, &minloc, &maxloc, Mat());
		cout << "Channel " << i << ":\n"
			<< "  Min value: " << minval << " at (" << minloc.x << "," << minloc.y << ")\n"
			<< "  Max value: " << maxval << " at (" << maxloc.x << "," << maxloc.y << ")\n";
	}
	Mat mean, stddev;
	//meanStdDev()第一个参数可多通道也可单通道
	vector<Mat> mv;
	split(image, mv);
	for (int i = 0; i < mv.size(); i++) {
		meanStdDev(mv[i], mean, stddev);
		cout << "mean: " << mean << "stddev: " << stddev << endl;
	}
}

void  Quick_demo::darw_demo(Mat& image) {
	Rect rect;
	rect.x =150;
	rect.y = 150;//x y为左上坐标
	rect.width = 300;
	rect.height = 300;

	Mat src;
	src.create(image.size(), image.type());
	Mat dst = Mat::zeros(image.size(), image.type());

	rectangle(src, rect, Scalar(0, 0, 255), 1, LINE_AA);
	circle(src, Point(350, 400), 150, Scalar(0, 0, 255), -1, LINE_AA);
	addWeighted(image, 0.7, src, 0.5, 0, dst);
	imshow("dst", dst);
}

void Quick_demo::random_draw(Mat& image) {
	RNG rng(12345);
	Mat random_back = Mat::zeros(Size(500, 500), CV_8UC3);
	while (true) {
		int c = waitKey(10);
		//先把画布的长宽拿出来再进行随机数生成
		//随机数生成放在while里
		int w = random_back.cols;
		int h = random_back.rows;
		int x = rng.uniform(0, w + 1);
		int y = rng.uniform(0, h + 1);
		int x1 = rng.uniform(0, w + 1);
		int y1 = rng.uniform(0, h + 1);
		int R = rng.uniform(0, 256);
		int B = rng.uniform(0, 256);
		int G = rng.uniform(0, 256);

		if (c == 27) {
			break;
		}
		//自动绘制随机颜色线条
		line(random_back, Point(x, y), Point(x1, y1), Scalar(R, B, G),1,LINE_AA,0);
		imshow("random", random_back);		
	}
}

Mat tmp;
void on_Mouse(int event, int x, int y, int flag, void* userdata) {
	static Point sp(-1, -1);//sp必须定义为全局的 因为在这个过程中无时无刻都在调用
	Mat image = *((Mat*)userdata);//想要用userdata不能直接解引用 因为是void* 要先强转
	if (event == EVENT_LBUTTONDOWN) {//鼠标左键按下 开始绘制(只需打印坐标点就行）
		//rectangle(*userdata, rect, Scalar(0, 0, 255), 1, LINE_AA);
		sp.x = x;
		sp.y = y;
		cout << "start ponit" << sp << endl;
	}

	else if (event == EVENT_MOUSEMOVE) {//鼠标移动 绘制（且要判断左键是否按下）
		if (sp.x > 0 && sp.y > 0) {
			tmp.copyTo(image);
			static Point ep(-1, -1);
			ep.x = x;
			ep.y = y;
			
			//宽高计算 末-初
			int dx = ep.x - sp.x;       
			int dy = ep.y - sp.y;

			Rect box(sp.x, sp.y, dx, dy);
			rectangle(image, box, Scalar(0, 0, 255), 1, LINE_AA);

			imshow("鼠标绘制", image);	
		} 
	}

	else if (event == EVENT_LBUTTONUP) {//左键释放 停止绘制
		if (sp.x > 0 && sp.y > 0) {

			static Point ep(-1, -1);
			ep.x = x;
			ep.y = y;

			//宽高计算 末-初
			int dx = ep.x - sp.x;
			int dy = ep.y - sp.y;

			//绘制
			if (dx > 0 && dy > 0) {
				Rect box(sp.x, sp.y, dx, dy);
				rectangle(image, box, Scalar(0, 0, 255), 1, LINE_AA);
				imshow("ROI区域", image(box)); 
				sp.x = -1;
				sp.y = -1;
			}
		}
	}
}
void Quick_demo::Mouse_draw(Mat& image) {
	namedWindow("鼠标绘制",WINDOW_FREERATIO);
	setMouseCallback("鼠标绘制", on_Mouse, (void*)&image);
	imshow("鼠标绘制", image);
	tmp = image.clone();
}

void Quick_demo::video_demo(Mat& image) {
	VideoCapture capture("E:/opencv4.8.0/study/1.png.png"); //从视频文件、图像序列或摄像头捕获视频的类的对象
	double frame_width = capture.get(CAP_PROP_FRAME_WIDTH);
	double frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int count = capture.get(CAP_PROP_FRAME_COUNT);
	Mat frame;
	while (1) {
		capture.read(frame);

		if (frame.empty()) {
			break;
		}

		imshow("frame", frame);
		//
		//Mouse_draw(image);
		int c = waitKey(1);
		if (c == 27) {
			break;
		}
	}
}
//彩色图像
void Quick_demo::equalizHist_demo(Mat& image) {
	imshow("均衡化前", image);
	//先创建clahe对象
	auto clahe = createCLAHE(2.0, Size(8, 8));
	//先转HSV
	Mat hsv;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	//通道分离(split)
	vector<Mat> channels;
	split(hsv, channels);
	//对v通道均衡化
	/*equalizeHist(channels[2], channels[2]);*/
	clahe->apply(channels[2], channels[2]);
	//合并通道
	Mat merge;
	cv::merge(channels, merge);
	//转RGB
	cvtColor(merge, image, COLOR_HSV2BGR);
	imshow("RGB均匀化后", image);
}

	
// 根据 bins 变化重新计算直方图和反向投影
void on_BinChange(int bins, void* userdata) {
	cv::Mat* hue = static_cast<cv::Mat*>(userdata);
	if (hue == nullptr) return;

	// 【1】参数准备
	int histSize = std::max(bins, 2);
	float hue_range[] = { 0, 180 };
	const float* ranges = hue_range;
	cv::Mat hist;

	// 【2】计算直方图并归一化
	cv::calcHist(hue, 1, 0, cv::Mat(), hist, 1, &histSize, &ranges, true, false);
	cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat());

	// 【3】计算反向投影
	cv::Mat backproj;
	cv::calcBackProject(hue, 1, 0, hist, backproj, &ranges, 1, true);

	//const std::string WINDOW_NAME1 = "Back Projection";
	// 【4】显示反向投影
	cv::imshow(WINDOW_NAME1, backproj);

	// 【5】绘制直方图的参数准备
	int w = 400;
	int h = 400;
	int bin_w = cvRound((double)w / histSize);
	cv::Mat histImg = cv::Mat::zeros(w, h, CV_8UC3);

	// 【6】绘制直方图
	for (int i = 0; i < bins; i++) {
		cv::rectangle(histImg, cv::Point(i * bin_w, h), cv::Point((i + 1) * bin_w, h - cvRound(hist.at<float>(i) * h / 255.0)), cv::Scalar(100, 123, 255), -1);
	}

	// 【7】显示直方图窗口
	cv::imshow("直方图", histImg);
}

// 反向投影函数
void Quick_demo::Back_projectoin(cv::Mat& inputImage) {
	if (inputImage.empty()) {
		std::cout << "无法读取图像" << std::endl;
		return;
	}

	// 转换为HSV颜色空间
	cv::Mat hsv;
	cv::cvtColor(inputImage, hsv, cv::COLOR_BGR2HSV);

	// 分离H通道
	std::vector<cv::Mat> hsvPlanes;
	cv::split(hsv, hsvPlanes);
	cv::Mat hue = hsvPlanes[0];

	int bins = 30;
	//const std::string WINDOW_NAME1 = "Back Projection";
	// 初始计算直方图和反向投影
	on_BinChange(bins, &hue);

	// 显示原始图像
	cv::imshow("Original Image", inputImage);

	// 创建滑动条
	cv::createTrackbar("* Hue bins", WINDOW_NAME1, &bins, 180, on_BinChange, &hue);
	cv::waitKey(0);
}

void Quick_demo::Filter_demo(Mat& image) {
	Mat dst_blur;
	Mat dst_gauss;
	//均值滤波
	blur(image, dst_blur, Size(1, 1));
	//高斯
	GaussianBlur(image, dst_gauss, Size(7, 7), 0, 0);
	imshow("jun zhi", dst_blur);
	imshow("gao si", dst_gauss);
}

void Quick_demo::Sobel_demo(Mat& image) {
	Mat dy;
	Mat dx;
	Sobel(image, dy, CV_32F, 1, 0, 5);
	Sobel(image, dx, CV_32F, 0, 1, 5);
	Mat dst;
	add(dy,dx,dst);
	imshow("Sobel", dst);
}

void Quick_demo::lapla_demo(Mat& image) {
    Mat dst;
	Laplacian(image, dst, CV_32F, 1);
	
	imshow("lapla", dst);
}

void Quick_demo::canny_demo(Mat& image) {//差值越小 边缘越细
	Mat dst;
	Canny(image, dst, 100, 108);
	imshow("canny", dst);
}

void Quick_demo::binarry_demo(Mat& image) {
	Mat dst;
	cvtColor(image, dst, COLOR_BGR2GRAY);
	//threshold(dst, dst, 120, 255,THRESH_BINARY_INV);

	adaptiveThreshold(dst, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 5, 0);
	imshow("binarry", dst);
}

void Quick_demo::fushi_pengzhang(Mat& image) {
	Mat dst;
	Mat se = getStructuringElement(0, Size(5,5));
	/*erode(image, dst, se,Point(-1,-1),2);
	dilate(dst, dst, se, Point(-1, -1), 2);*/
	//开运算(先腐蚀后膨胀)
	/*morphologyEx(image, dst, MORPH_OPEN, se);
	imshow("ss", dst);*/
	//闭运算
	//morphologyEx(image, dst, MORPH_CLOSE, se);
	//梯度
	morphologyEx(image, dst, MORPH_GRADIENT, se);
	imshow("ss", dst);
}